package com.axapoint.opoc.core.dao;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;
import org.joda.time.DateTime;
import org.joda.time.Interval;

import com.anasoft.os.daofusion.bitemporal.WrappedBitemporalProperty;
import com.anasoft.os.daofusion.bitemporal.WrappedValueAccessor;

@Entity
@Table(name = "UnitRelation")
public class UnitRelation extends ObjectRegistration<UnitRelation, UnitRelation.Value, UnitRelation.Bitemporal> {
	private static final long serialVersionUID = 9168710745762860711L;

	@Entity
	@Table(name = "UnitRelationValues")
	public static final class Value extends _Value<UnitRelation, Bitemporal> {
		private static final long serialVersionUID = 1L;

		@SuppressWarnings("deprecation")
		@ManyToOne(fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="unitRelationTypeId")
		private UnitRelationType type;

		@SuppressWarnings("deprecation")
		@ManyToOne(fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="organizationId")
		private Organization organization;
		
		@SuppressWarnings("deprecation")
		@ManyToOne(fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="parentId")
		private Unit parent;
		
		@SuppressWarnings("deprecation")
		@ManyToOne(fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="childId")
		private Unit child;
		
		protected Value() {};
		Value(UnitRelation ur, String systemId, String userId, Operation operation, String comment, Value value) {
			super(ur, systemId, userId, operation, comment);
			this.type = value.type;
			this.organization = value.organization;
			this.parent = value.parent;
			this.child = value.child;
		}
		public Value(UnitRelationType type, Organization organization, Unit parent, Unit child) {
			super();
			this.type = type;
			this.organization = organization;
			this.parent = parent;
			this.child = child;
		}
		@Override
		public String diff(_Value<UnitRelation, Bitemporal> otherValue, DateTime t0, DateTime t1) {
			StringBuffer sb = new StringBuffer();
			
			UnitRelationType.Bitemporal l0 = type.getValues().get(t0);
			UnitRelationType.Bitemporal l1 = type.getValues().get(t1);

			if (!l0.getValue().getEntity().equals(l1.getValue().getEntity()) ||
				!l0.getValue().equals(l1.getValue())) {
				sb.append("UnitRelation.type: ");
				sb.append('[').append(l0.getValue().getEntity().getUuid()).append("] ").append(l0.getValue().getName()).append(" - ");
				sb.append('[').append(l0.getValue().getEntity().getUuid()).append("] ").append(l1.getValue().getName()).append('\n');
			}
			
			Organization.Bitemporal o0 = organization.getValues().get(t0);
			Organization.Bitemporal o1 = organization.getValues().get(t1);

			if (!o0.getValue().getEntity().equals(o1.getValue().getEntity()) ||
				!o0.getValue().equals(o1.getValue())) {
				sb.append("UnitRelation.organization: ");
				sb.append('[').append(o0.getValue().getEntity().getUuid()).append("] ").append(o0.getValue().getName()).append(" - ");
				sb.append('[').append(o0.getValue().getEntity().getUuid()).append("] ").append(o1.getValue().getName()).append('\n');
			}

			Unit.Bitemporal p0 = parent.getValues().get(t0);
			Unit.Bitemporal p1 = parent.getValues().get(t1);

			if (!p0.getValue().getEntity().equals(p1.getValue().getEntity()) ||
				!p0.getValue().equals(p1.getValue())) {
				sb.append("UnitRelation.parent: ");
				sb.append('[').append(p0.getValue().getEntity().getUuid()).append("] ").append(p0.getValue().getName()).append(" - ");
				sb.append('[').append(p0.getValue().getEntity().getUuid()).append("] ").append(p1.getValue().getName()).append('\n');
			}

			Unit.Bitemporal c0 = child.getValues().get(t0);
			Unit.Bitemporal c1 = child.getValues().get(t1);

			if (!c0.getValue().getEntity().equals(c1.getValue().getEntity()) ||
				!c0.getValue().equals(p1.getValue())) {
				sb.append("UnitRelation.type: ");
				sb.append('[').append(p0.getValue().getEntity().getUuid()).append("] ").append(p0.getValue().getName()).append(" - ");
				sb.append('[').append(p0.getValue().getEntity().getUuid()).append("] ").append(p1.getValue().getName()).append('\n');
			}

			return sb.toString();
		}
		@Override
		public String toString() {
			StringBuffer sb = new StringBuffer();
			sb.append(super.toString());
			sb.append("  type=").append(type.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			sb.append("  organization=").append(organization.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			sb.append("  parent=").append(parent.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			sb.append("  child=").append(child.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			
			return sb.toString();
		}
		public UnitRelationType getType() {
			return type;
		}
		protected void setType(UnitRelationType type) {
			this.type = type;
		}
		public Organization getOrganization() {
			return organization;
		}
		protected void setOrganization(Organization organization) {
			this.organization = organization;
		}
		public Unit getParent() {
			return parent;
		}
		protected void setParent(Unit parent) {
			this.parent = parent;
		}
		public Unit getChild() {
			return child;
		}
		protected void setChild(Unit child) {
			this.child = child;
		}
	}
	
	@Entity
	@Table(name = "UnitRelationBitemporal")
	public static final class Bitemporal extends _Bitemporal<Bitemporal, Value> {
		private static final long serialVersionUID = -2132187534862370208L;
		
		protected Bitemporal() {}
	    private Bitemporal(UnitRelation.Value value, Interval validityInterval) {
	        super(value, validityInterval);
	    }
	    
	    @Override
	    public Bitemporal copyWith(Interval validityInterval) {
	    	return new Bitemporal(value, validityInterval);
	    }
	}

	public UnitRelation() {
		super();
	}
	
    public WrappedBitemporalProperty<Value, Bitemporal> getValues() {
    	return new WrappedBitemporalProperty<Value, Bitemporal>(values, new WrappedValueAccessor<Value,Bitemporal>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Bitemporal wrapValue(Value value, Interval validityInterval) {
				Bitemporal bi = new Bitemporal(value, validityInterval);
				return bi;
			}
    	});
    }
}
